#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Window.h"

#include "Class.h"
#include "Error.h"
#include "WindowPort.h"
#include "WindowSystem.h"
#include "ObjectTable.h"
#include "ClassManager.h"
#include "Storage.h"
#include "View.h"
#include "Document.h"
#include "Env.h"
#include "Look.h"
#include "Buttons.h"
#include "String.h"
#include "System.h"
#include "ET_stdio.h"

#include "MenuBar.h"

extern bool gWinDebug;

Token
	gToken,
	gLastClickToken;
Window *gWindow;
bool    gDragDropCode;
Rectangle gDropRect;


//---- Window ------------------------------------------------------------------

NewMetaImpl(Window,Clipper, (TB(active), TP(manager), TP(portDesc), TP(icon)));

Window::Window(Manager *m, const Point &e, WindowFlags f, VObject *in,
				const char *title) : Clipper(0, gPoint_1)
{
	Init(m, in, eWinStandard, eCrsBoldArrow, f, title, e);
}

Window::Window(Manager *m, VObject *vp, WindowType wt, WindowFlags f, const Point &e,
				const char *title) : Clipper(0, gPoint_1)
{
	Init(m, vp, wt, eCrsBoldArrow, f, title, e);
}

void Window::Init(Manager *eh, VObject *in, WindowType wt, GrCursor curs,
				WindowFlags wf, const char *t, const Point &e)
{
	wtype= wt;
	wflags= wf;
	manager= eh;
	ResetFlag(eVObjOpen);
	cursor= curs;
	bgcolor= gInkNone;
	bgcolor= gLook->GetBackgroundColor();
	SetDeleteVop(TRUE);
	inner= in;
	contentRect.extent= e;
	VObject *v= gWindowSystem->MakeWindowBorder(wtype, inner, (char*)t,
					wf & eWinBlock, wf & eWinFixed);
	Add(v);
}

Window::~Window()
{
	if (gWindow == this)
		gWindow= 0;
	if (vop != inner) {
		if (deleteVop)
			SafeDelete(inner);
		deleteVop= TRUE;
	}
	if (icon) {
		Window *tmp= icon;
		icon= 0;
		delete tmp;
	}
	if (portDesc) {
		if (gSystem)
			gSystem->AddCleanupObject(portDesc);
		portDesc= 0;
	}
}

void Window::SetPortDesc(Port *newport)
{
	portDesc= (WindowPort*)newport;
}

void Window::ExtentChanged(VObject*)
{
	// SetExtent(Max(GetMinSize().Extent(), GetExtent()));
	SetExtent(GetMinSize().Extent());
}

EvtHandler *Window::GetNextHandler()
{
	return manager;
}

void Window::SetNextManager(Manager *m)
{
	manager= m;
}

void Window::InvalidateRect(Rectangle r)
{
	MakePort()->InvalidateRect(r);
}

void Window::SetExtent(Point e)
{
	MakePort()->SetExtent(e);
}

void Window::SetOrigin(Point at)
{
	VObject::SetOrigin(gPoint0);
	MakePort()->SetOrigin(GetRect().origin+at);
}

void Window::Focus()
{
	if (IsOpen() && portDesc) {
		GrSetPort(portDesc);
		GrInitClip();
		GrClipFurther(contentRect);
		GrTranslate(Offset());
	}
}

void Window::FocusNotify(bool f)
{
	gWindow= this;
	if (portDesc == 0)
		return;
	GrSetPort(portDesc);
	active= f;
}

void Window::InputNotify(Token *t)
{
	gWindow= this;
	if (portDesc == 0)
		return;
	GrSetPort(portDesc);

	gToken= *t;

	switch (t->Code) {
	case eEvtRightButton:
	case eEvtMiddleButton:
	case eEvtLeftButton:
		if (!(t->Flags & eFlgButDown))
			gLastClickToken= *t;
		break;
	}

	if (t->Code == eEvtEnter || t->Code == eEvtExit) {
		active= t->Code == eEvtEnter;
	} else if (t->IsKey() && t->Flags == (eFlgShiftKey|eFlgCntlKey|eFlgMetaKey)) {
		switch (t->MapToAscii()) {
		case 'p':
			Print();
			break;
		case 's':
			gClassManager->InstanceStatistics();
			break;
		case 'i':
			gClassManager->InstanceStatistics(TRUE);
			break;
		case 'l':
			ToggleLook();
			{
				vop->SetExtent(GetExtent());
				vop->SetOrigin(gPoint0);
				ForceRedraw();
			}
			break;
		case 'e':
			Error("InputNotify", "forced invocation of ErrorHandler");
			break;
		case 'm':
			Storage::Statistics();
			break;
		}
		return;
	}

	if (manager && (gToken.IsKey() || gToken.IsFunctionKey() || gToken.IsCursorKey())) {
		manager->InputKbd(gToken);
	} else {
		PerformCommand(DispatchEvents(gToken.Pos, gToken, this));
	}
}

void Window::OpenNotify(const Point &e)
{
	if (gWinDebug && portDesc) {
		Rectangle r(GetRect());
		fprintf(stderr, "OpenNotify: %d %d %d %d\n", r.origin.x, r.origin.y,
					r.extent.x, r.extent.y);
	}

	if (gWinDebug) fprintf(stderr, "OpenNotify: %d %d\n", e.x, e.y);
	if (portDesc) {
		gWindow= this;
		GrSetPort(portDesc);
		if (! IsOpen())
			Clipper::Open(TRUE);
		VObject::SetExtent(e);
		if (vop)
			vop->SetContentRect(Rectangle(gPoint0, e), TRUE);
		UpdateEvent();
	}
}

void Window::CloseNotify()
{
	if (IsOpen()) {
		if (gWinDebug) fprintf(stderr, "CloseNotify\n");
		Clipper::Open(FALSE);
	}
}

void Window::ResizeNotify(const Point &e)
{
	if (IsOpen())
		if (gWinDebug) fprintf(stderr, "ResizeNotify: %d %d\n", e.x, e.y);
}

GrCursor Window::GetCursor(Point)
{
	return cursor;
}

WindowPort *Window::MakePort()
{
	if (portDesc == 0)
		portDesc= gWindowSystem->MakeWindow(this, wtype, cursor);
	return portDesc;
}

void Window::Open(bool mode)
{
	if (mode)   // open
		OpenAt(GetRect().origin);
	else if (portDesc)
		portDesc->Hide();
}

void Window::OpenAt(const Point &pp, VObject *fp, bool resize, bool block)
{
	Point p(pp);
	WindowPort *fatherport= 0;

	if (IsOpen()) {
		Top();
		return;
	}
	if (fp) {
		Window *w= fp->GetWindow();
		if (w)
			fatherport= (WindowPort*) (w->portDesc);
		p= fp->GetPortPoint(p);
	}

	GrSetPort(MakePort());

	if (resize)
		contentRect.extent= gPoint0;

	Point e= Max(GetMinSize().Extent(), GetExtent());
	if (manager && fp)
		p-= manager->GetInitialPos(e);

	if (gWinDebug)
		fprintf(stderr, "\nShow: %d %d %d %d\n", p.x, p.y, e.x, e.y);

	portDesc->Show(fatherport, Rectangle(p, e), block || (wflags & eWinBlock));

	if (fatherport)
		GrSetPort(fatherport);
}

void Window::Iconize()
{
	if (icon == 0)
		icon= MakeIcon();
	if (icon)
		icon->icon= this;
	MakePort()->Iconize();
}

Window *Window::MakeIcon()
{
	if (manager) {
		VObject *v= manager->DoMakeIconContent();
		if (v)
			return new Window(manager, v, eWinIcon, eWinFixed, Point(64), 
														BaseName(gProgPath));
	}
	return 0;
}

Rectangle Window::ScreenRect()
{
	return WindowSystem::screenRect - GetRect().origin;
}

void Window::ReadEvent(Token &t)
{
	MakePort()->GetEvent(&gToken);
	t= gToken;
}

Rectangle Window::GetRect()
{
	//Rectangle r(MakePort()->GetRect());
	//fprintf(stderr, "GetRect: %d %d %d %d\n", r.origin.x, r.origin.y,
	//                                                r.extent.x, r.extent.y);
	return MakePort()->GetRect();
}

void Window::Top()
{
	MakePort()->Top();
}

void Window::Bottom()
{
	MakePort()->Bottom();
}

void Window::Bell(long d)
{
	MakePort()->Bell(d);
}

void Window::SetMousePos(const Point &p)
{
	MakePort()->SetMousePos(p);
}

void Window::Fullscreen(bool g)
{
	MakePort()->Fullscreen(g);
}

void Window::SetTitle(const char *tit)
{
	MakePort()->SetTitle((char*)tit);
}

int Window::GetId()
{
	return MakePort()->DevGetId();
}

void Window::InspectorId(char *buf, int sz)
{
	if (manager)
		manager->InspectorId(buf, sz);
	else
		Clipper::InspectorId(buf, sz);
}

int Window::DragDrop(int code, const Point &localpos, Window *w)
{
	if (w)
		return gWindowSystem->DragDrop(code, localpos+w->GetRect().origin);
	return 0;
}

int Window::DragDropNotify(int code, const Point &globalpos, Rectangle *rp)
{
	gDragDropCode= FALSE;
	gDropRect= gRect0;
	Token t((EventCodes)(eEvtLocMoveBut+2), (EventFlags)code);
	t.Pos= globalpos - GetRect().origin;
	PerformCommand(DispatchEvents(t.Pos, t, this));
	if (rp) {
		gDropRect.origin+= GetRect().origin;
		*rp= gDropRect;
	}
	return gDragDropCode;
}

